

#include "ls_common.h"
#include "ls_dc357.h"
#include "ls_dc357_common.h"
#include "ls_spi.h"


#define DBG_TAG "DC357"
#include "ls_trace_common.h"

//
#define LS_GPIO_REST_750        GPIO_PIN_6
#define LS_GPIO_INFORM          GPIO_PIN_13
#define LS_GPIO_WAKEUP          GPIO_PIN_14
#define LS_GPIO_ACK             GPIO_PIN_15

uint16_t  list_cnt = 0;

static dc357_recvOnTransPort   grecvOnData = NULL;
static struct bflb_device_s   *gpiodev;


static SemaphoreHandle_t  sem_slave_ack;
static SemaphoreHandle_t  sem_slave_wekup;

static SemaphoreHandle_t  muxte_send_list;

static uint8_t  startup = 1;
static uint8_t  net_status = 0;
static uint8_t  setup_chn_status = 0; // 0 表示正常状态 1 表示接收图片得状态
static uint8_t  dc750_startup = 0;
//发送缓冲区
static ls_list_head dc357_send_cmd_list;


static pthread_t dc357_send_pid;

/// @brief 
typedef struct lsSPI_SEND_DATA_ITEM_S{
    ls_list_head    list;
    uint16_t        danlen;
    uint8_t         channel;
    char            data[0];
}SPI_SEND_DATA_ITEM_S;



static void ls_send_simple_cmd(enCmd_type cmd_type);
static void __ls_dc357_cmd_handle(Trans_pkg *pPkg);

/// @brief 
/// @return 
static SPI_SEND_DATA_ITEM_S *spi_item_malloc(uint16_t len)
{
    SPI_SEND_DATA_ITEM_S  *Item=NULL;
    Item =(SPI_SEND_DATA_ITEM_S  *)kmalloc(sizeof(SPI_SEND_DATA_ITEM_S)+len);
    memset(Item,0,sizeof(SPI_SEND_DATA_ITEM_S));
    return Item;
}



/// @brief 
/// @param value 
static void ls_inform_set(uint8_t value)
{
    if(value)
    {
        bflb_gpio_set(gpiodev,LS_GPIO_INFORM);
    }
    else
    {
        bflb_gpio_reset(gpiodev,LS_GPIO_INFORM);
    }
}


/// @brief 
static void ls_wekup_wifi()
{
    xSemaphoreGive(sem_slave_wekup);
}



uint32_t ok_cnt = 0;
uint32_t err_cnt = 0;

static ATTR_NOINIT_PSRAM_SECTION Trans_pkg recvPkg={0};
static ATTR_NOINIT_PSRAM_SECTION Trans_pkg sendPkg={0};
/// @brief 
static void* ls_dc357_sed_data_proc()
{
    LS_TRACE_DEBUG("spi send proc start!");
    
    SPI_SEND_DATA_ITEM_S *Item = NULL;
    while (1)
    {
        /* 获取信号量成功有两种可能，1.tx_fifo中有数据 2.从机有数据要发送。*/
        xSemaphoreTake(sem_slave_wekup,portMAX_DELAY);
        ls_inform_set(1);
        Item = NULL;

        //是否有数据发送 CMD 通道
        if(!list_empty(&dc357_send_cmd_list))
        {
            //for 顺便判断不为空
            list_for_each_entry(Item,&dc357_send_cmd_list,list)
            {
                //
                //LS_TRACE_DEBUG("data_len:%d",Item->danlen);
                memcpy(sendPkg.buf,Item->data,Item->danlen);
                sendPkg.magic   =  TX_MAGIC;
                sendPkg.len     =  Item->danlen;
                sendPkg.channel =  Item->channel;
                break;
            }
        }

        //
        BaseType_t  SemRet = xSemaphoreTake(sem_slave_ack,1000);
        if(SemRet)
        {
            //
            // LS_TRACE_DEBUG("spi send :%u",sizeof(Trans_pkg));
            spi_poll_u8(&sendPkg,&recvPkg,sizeof(Trans_pkg));

            SemRet = xSemaphoreTake(sem_slave_ack,1000);
            if(pdPASS != SemRet)
            {
              ls_inform_set(0);
              ls_wekup_wifi();
              LS_TRACE_ERROR("slave no data ack!");
              continue;               
            }

            if(Item != NULL && Item != &dc357_send_cmd_list)
            {
                xSemaphoreTake(muxte_send_list,portMAX_DELAY);
                list_del(&Item->list);
                list_cnt--;
                kfree(Item);
                xSemaphoreGive(muxte_send_list);
                LS_TRACE_DEBUG("delete msg:%p %d",Item,list_cnt);
            }
        }
        else 
        {
            ls_inform_set(0);
            ls_wekup_wifi();
            LS_TRACE_ERROR("slave no inform ack!");
            continue; 
        }

        ls_inform_set(0);

        //处理接收数据
        if(recvPkg.magic == RX_MAGIC && recvPkg.len > 0 )
        {
            // LS_TRACE_DEBUG("recvPkg.magic:%x",recvPkg.magic);
            // LS_TRACE_DEBUG("recvPkg.channel:%x",recvPkg.channel);
            
            //这两个通道的数据需要转发处理
            if(grecvOnData && (recvPkg.channel == P2P_CHANNEL_CMD || recvPkg.channel == P2P_CHANNEL_VIDEO))
            {
                grecvOnData(recvPkg.channel,recvPkg.buf,recvPkg.len);
            }
            else
            {
                __ls_dc357_cmd_handle(&recvPkg);
            }
        }
        else if(recvPkg.magic != 0)
        {
            LS_TRACE_ERROR("recv data frame error:%x!",recvPkg.magic);
        }
        usleep(20);  
    }

    LS_TRACE_DEBUG("spi send proc end!");
    return NULL;
}




/// @brief 
/// @param tx 
/// @param body 
/// @param cmd_type 
/// @return 
static int16_t __ls_cmd_data_fill(void *body,uint8_t *channel,enCmd_type cmd_type,const void *content)
{
    int16_t len = 0;

    switch (cmd_type)
    {
        case CMD_WIFI_TO_M4_P2P_CLIENT_CONNECT:
        {
            CMD_CLIENT_CONENCT_WIFI_TO_M4_S  *cmd=(CMD_CLIENT_CONENCT_WIFI_TO_M4_S  *)body;
            *channel = P2P_CHANNEL_SETUP;
            cmd->head.cmd_type = CMD_WIFI_TO_M4_P2P_CLIENT_CONNECT;
            cmd->head.cmd_len = sizeof(cmd->body);
            cmd->body.session_id = 0;
            len = sizeof(CMD_CLIENT_CONENCT_WIFI_TO_M4_S); 
        }   
        break;
        case CMD_WIFI_TO_M4_P2P_CLIENT_CLOSE:
        {
            *channel = P2P_CHANNEL_SETUP;
            CMD_CLIENT_CLOSE_WIFI_TO_M4_S  *cmd=(CMD_CLIENT_CLOSE_WIFI_TO_M4_S  *)body;
            cmd->head.cmd_type = CMD_WIFI_TO_M4_P2P_CLIENT_CLOSE;
            cmd->head.cmd_len = sizeof(cmd->body);
            cmd->body.session_id = 0;
            len = sizeof(CMD_CLIENT_CLOSE_WIFI_TO_M4_S); 
        }
        break;
        case CMD_WIFI_TO_M4_P2P_WIFI_CONNECT:
        {
            *channel = P2P_CHANNEL_SETUP;
            CMD_CLIENT_STATUS_WIFI_TO_M4_S  *cmd=(CMD_CLIENT_STATUS_WIFI_TO_M4_S  *)body;
            cmd->head.cmd_type = CMD_WIFI_TO_M4_P2P_WIFI_CONNECT;
            cmd->head.cmd_len = sizeof(cmd->body);
            cmd->body.session_id = (int)content;
            len = sizeof(CMD_CLIENT_STATUS_WIFI_TO_M4_S);
        }
        break;
        case CMD_M4_TO_WIFI_P2P_CLOSE:
        {

        }
        break;
        case CMD_M4_WIFI_DEV_INFO:
        {

        }
        break;
        case CMD_M4_TO_WIFI_ALARM:
        {
            
        }
        break;
        case CMD_M4_WIFI_CHECK_UPDATE:
        {
            
        }
        break;

        case CMD_M4_TO_WIFI_WIFIID:
        {
            
        }      
        break;
        default:
            LS_TRACE_ERROR("cmd error!");
        break;
    }
    
    return len;
}




/// @brief 
/// @param cmd_type 
static void ls_send_simple_cmd(enCmd_type cmd_type)
{
    //
    uint8_t channel = 0;
    unsigned char buf[SPI_BUF_PKG_MAX_LEN];
    int16_t data_len = __ls_cmd_data_fill(buf,&channel,cmd_type,NULL);
    
    xSemaphoreTake(muxte_send_list,portMAX_DELAY);
    //
    SPI_SEND_DATA_ITEM_S *Item = spi_item_malloc(data_len);
    Item->danlen = data_len;
    Item->channel= channel;
    memcpy(Item->data,buf,data_len);
    //
    list_cnt++;
    list_add_tail(&Item->list,&dc357_send_cmd_list);
    xSemaphoreGive(muxte_send_list);
    LS_TRACE_DEBUG("add msg:%p %d",Item,list_cnt);

    ls_wekup_wifi();
}







/******************************************************************************************************************/

/// @brief 
/// @param irq 
/// @param arg 
static void ls_dc357_gpio_isr(int irq, void *arg)
{
    bool intstatus = bflb_gpio_get_intstatus(gpiodev,LS_GPIO_WAKEUP);
    if(intstatus)
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        xSemaphoreTakeFromISR(sem_slave_ack,&xHigherPriorityTaskWoken);
        int32_t ret = xSemaphoreGiveFromISR(sem_slave_wekup, &xHigherPriorityTaskWoken);
        if (ret == pdPASS) {
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }

        bflb_gpio_int_clear(gpiodev, LS_GPIO_WAKEUP);
        // LS_TRACE_DEBUG("LS_GPIO_WAKEUP irq");
    }


    intstatus = bflb_gpio_get_intstatus(gpiodev,LS_GPIO_ACK);
    if(intstatus)
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        int32_t ret = xSemaphoreGiveFromISR(sem_slave_ack, &xHigherPriorityTaskWoken);
        if (ret == pdPASS) {
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }

        bflb_gpio_int_clear(gpiodev, LS_GPIO_ACK);
        // LS_TRACE_DEBUG("LS_GPIO_ACK irq");
    }


    // LS_TRACE_DEBUG("irq:%d",irq);
}



/// @brief 
/// @param data 
static void __ls_dc357_cmd_handle(Trans_pkg *pPkg)
{
    if(setup_chn_status)
    {
        if(pPkg->len < 1024)
        {
            setup_chn_status = 0;
            LS_TRACE_DEBUG("img file upload end!");
        }

        if(grecvOnData)grecvOnData(CUSTOM_CHANNEL_IMG,pPkg->buf,pPkg->len);
    }
    else
    {   
        stCmd_head *head =(stCmd_head *)pPkg->buf;
        LS_TRACE_INFO("head->cmd_type:%x",head->cmd_type);
        switch (head->cmd_type)
        {
        case CMD_M4_WIFI_CHECK_UPDATE:
        {
            dc750_startup = 1;
            ls_dc357_send_wifi_status(net_status);
        }
        break;
        case CMD_M4_TO_WIFI_ALARM:
        {
            LS_TRACE_DEBUG("wifi alarm!");
        }
        break;
        case CMD_M4_TO_WIFI_FILE:
        {
            setup_chn_status = 1;
            if(grecvOnData)grecvOnData(CUSTOM_CHANNEL_IMG,NULL,0);
        }
        break;
        case CMD_M4_TO_WIFI_SLEEP:

        break;
        case CMD_M4_TO_WIFI_WIFIID:
            // LS_TRACE_DEBUG("head->buf :%s %d %d!", head->buf+1,pPkg->len,sizeof(stCmd_head));
            // for (uint8_t i = 0; i < 8; i++)
            // {
            //     LS_TRACE_DEBUG("%x",head->buf[i]);
            // }
            if(grecvOnData)grecvOnData(CUSTOM_CHANNEL_WIFI,head->buf+4,pPkg->len-sizeof(stCmd_head)-4);
        break;
        default:
            break;
        }

    }

}


/// @brief 
/// @param xTimer 
static void _ls_dc750_startup_check(TimerHandle_t xTimer)
{
    static uint8_t dc750_startup_cnt = 0;
    if(!dc750_startup && dc750_startup_cnt < 64)
    {
        dc750_startup_cnt++;
        ls_dc357_p2p_client_close();
    }
    else
    {
        LS_TRACE_INFO("dc750_startup_cnt:%d",dc750_startup_cnt);
        xTimerDelete(xTimer,pdFALSE);
    }
}
/******************************************************************************************************************/
/// @brief 
void ls_dc357_init(dc357_recvOnTransPort l)
{
    grecvOnData = l;
    //
    LS_TRACE_DEBUG("dc357 init");
    //spi
    ls_spi_init();

    //发送缓存区
    INIT_LIST_HEAD(&dc357_send_cmd_list);

    //应答信号
    sem_slave_ack=xSemaphoreCreateBinary();

    sem_slave_wekup=xSemaphoreCreateCounting(10,0);

    muxte_send_list=xSemaphoreCreateMutex();

    pthread_create(&dc357_send_pid,NULL,ls_dc357_sed_data_proc,NULL);


    gpiodev = bflb_device_get_by_name("gpio");

    bflb_gpio_int_init(gpiodev, LS_GPIO_WAKEUP, GPIO_INT_TRIG_MODE_SYNC_FALLING_EDGE);
    bflb_gpio_init(gpiodev, LS_GPIO_WAKEUP, GPIO_INPUT  | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_int_mask(gpiodev, LS_GPIO_WAKEUP, false);

    //
    
    bflb_gpio_int_init(gpiodev, LS_GPIO_ACK, GPIO_INT_TRIG_MODE_SYNC_FALLING_EDGE);
    bflb_gpio_init(gpiodev, LS_GPIO_ACK, GPIO_INPUT  | GPIO_SMT_EN | GPIO_DRV_3);
    bflb_gpio_int_mask(gpiodev, LS_GPIO_ACK, false);
    LS_TRACE_DEBUG("LS_GPIO_ACK-----GPIO_DRV_0");

    bflb_gpio_init(gpiodev, GPIO_PIN_32, GPIO_INPUT  | GPIO_SMT_EN | GPIO_DRV_1);
    bflb_gpio_init(gpiodev, GPIO_PIN_33, GPIO_INPUT | GPIO_PULLDOWN | GPIO_SMT_EN | GPIO_DRV_1);


    bflb_irq_attach(gpiodev->irq_num, ls_dc357_gpio_isr, gpiodev);
    bflb_irq_enable(gpiodev->irq_num); 


    bflb_gpio_init(gpiodev, LS_GPIO_INFORM, GPIO_OUTPUT | GPIO_PULLDOWN | GPIO_SMT_EN | GPIO_DRV_0);
    LS_TRACE_DEBUG("LS_GPIO_INFORM-----GPIO_DRV_0_1");
    ls_inform_set(0);

    ls_dc357_p2p_client_close();
    //
    // TimerHandle_t timeId = xTimerCreate("750_check",3000,pdTRUE,(void*)3,_ls_dc750_startup_check);
    // xTimerStart(timeId,3000);
}



/// @brief 
void ls_dc357_deinit()
{


}




/// @brief wifi and server connect status
/// @param status 
void ls_dc357_send_wifi_status(uint8_t status)
{
    LS_TRACE_DEBUG("wifi status:%d",status);
    net_status = status;
    //
    uint8_t channel = 0;
    unsigned char buf[SPI_BUF_PKG_MAX_LEN];

    int16_t data_len = __ls_cmd_data_fill(buf,&channel,CMD_WIFI_TO_M4_P2P_WIFI_CONNECT,status);

    //
    SPI_SEND_DATA_ITEM_S *Item = spi_item_malloc(data_len);
    Item->danlen = data_len;
    Item->channel= channel;
    memcpy(Item->data,buf,data_len);

    //
    xSemaphoreTake(muxte_send_list,portMAX_DELAY);
    
    list_add_tail(&Item->list,&dc357_send_cmd_list);
    list_cnt++;
    xSemaphoreGive(muxte_send_list);
    LS_TRACE_DEBUG("add msg:%p %d",Item,list_cnt);   
    ls_wekup_wifi();
}



/// @brief P2P 客户端连接
void ls_dc357_p2p_client_conenct()
{
    ls_send_simple_cmd(CMD_WIFI_TO_M4_P2P_CLIENT_CONNECT);
}


/// @brief P2P 客户端断开
void ls_dc357_p2p_client_close()
{
    ls_send_simple_cmd(CMD_WIFI_TO_M4_P2P_CLIENT_CLOSE);
}




/// @brief 
void ls_dc357_p2p_send_cmd(uint8_t channel,const char *data,uint32_t datalen)
{
    int16_t data_len = datalen > SPI_BUF_PKG_MAX_LEN ? SPI_BUF_PKG_MAX_LEN:datalen;
    unsigned char buf[SPI_BUF_PKG_MAX_LEN];

    xSemaphoreTake(muxte_send_list,portMAX_DELAY);
    
    SPI_SEND_DATA_ITEM_S *Item = spi_item_malloc(data_len);
    Item->danlen = data_len;
    Item->channel= channel;
    memcpy(Item->data,data,data_len);

    //
    
    list_add_tail(&Item->list,&dc357_send_cmd_list);
    list_cnt++;
    xSemaphoreGive(muxte_send_list);
    LS_TRACE_DEBUG("add msg:%p %d",Item,list_cnt);
    ls_wekup_wifi();
}




void dc357_test(int argc, char **argv)
{
   if(argc > 1 && atoi(argv[1]) == 1)
   {
     ls_dc357_p2p_client_close();
   }
   else if(argc > 1 && atoi(argv[1]) == 2)
   {
     ls_dc357_send_wifi_status(2);
   }
   else if(argc > 1 && atoi(argv[1]) == 3)
   {
     ls_dc357_p2p_client_conenct();
   } 

}

SHELL_CMD_EXPORT_ALIAS(dc357_test,dc357_test,cmd_dc357_test);